﻿using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using RollBackLib.src.utils;
using RollbackLib.src.utils;

namespace RollBackLib.src.core
{
    public class EmailAndSmtpTable
    {
        public static string m_EmailTableFile;
        public static string m_SmtpTableFile;
        private static readonly ILog log = LogManager.GetLogger(typeof(EmailAndSmtpTable));
        
        // password for the encryption,
        private static string password = "fr33b13i@_2#";

        public EmailTable m_emailTable = new EmailTable();
        public SMTPTable m_smtpTable = new SMTPTable();


        /// <summary>
        /// The most popular emails and their smtp host/port
        /// </summary>
        public static String gmailHost = "smtp.gmail.com";
        public static int gmailPort = 587;
        public static String gmailSmtpKey = gmailHost + ":" + gmailPort;

        public static String yahooHost = "smtp.mail.yahoo.com";
        public static int yahooPort = 25;
        public static String yahooSmtpKey = yahooHost + ":" + yahooPort;

        public static String hotmailHost = "smtp.live.com";
        public static int hotmailPort = 25;
        public static String hotmailSmtpKey = hotmailHost + ":" + hotmailPort;

        /// <summary>
        /// A cached copy of the email and smtp table.
        /// </summary>
        private static EmailAndSmtpTable m_cachedEmailAndSmtpTable = null;

        public static void save(EmailAndSmtpTable o)
        {
            lock (typeof(EmailAndSmtpTable))
            {
                if (m_EmailTableFile != null && m_SmtpTableFile != null)
                {
                    // Encrypt the password first and save a copy of the un-encrypted password
                    Crypto crypto = new Crypto(password);
                    foreach (EmailEntry entry in o.m_emailTable.m_EmailList)
                    {
                        String encrypted = crypto.Encrypt(entry.m_Password);
                        entry.setEncryptedPassword(encrypted);
                    }
                    RollbackLib.src.utils.XMLUtils.SaveObjectToXML(m_EmailTableFile, o.m_emailTable);
                    RollbackLib.src.utils.XMLUtils.SaveObjectToXML(m_SmtpTableFile, o.m_smtpTable);

                    // revert the encryption by restoring the locally saved un-encrypted password
                    foreach (EmailEntry entry in o.m_emailTable.m_EmailList)
                    {
                        entry.restoreUnEncryptedPassword();
                    }
                }
                m_cachedEmailAndSmtpTable = o;
            }
        }


        /// <summary>
        /// Save the email table;
        /// </summary>
        /// <param name="emailTable"></param>
        public static void save(EmailTable emailTable)
        {
            lock (typeof(EmailAndSmtpTable))
            {
                if (m_EmailTableFile != null)
                {
                    // Encrypt the password first and save a copy of the un-encrypted password
                    Crypto crypto = new Crypto(password);
                    foreach (EmailEntry entry in emailTable.m_EmailList)
                    {
                        String encrypted = crypto.Encrypt(entry.m_Password);
                        entry.setEncryptedPassword(encrypted);
                    }
                    RollbackLib.src.utils.XMLUtils.SaveObjectToXML(m_EmailTableFile, emailTable);

                    // revert the encryption by restoring the locally saved un-encrypted password
                    foreach (EmailEntry entry in emailTable.m_EmailList)
                    {
                        entry.restoreUnEncryptedPassword();
                    }
                }
                if (m_cachedEmailAndSmtpTable != null)
                {
                    m_cachedEmailAndSmtpTable.m_emailTable = emailTable;
                }
            }
        }

        /// <summary>
        /// Load the email and smtp table from the file
        /// </summary>
        /// <returns></returns>
        public static EmailAndSmtpTable load( )
        {
            lock (typeof(EmailAndSmtpTable))
            {
                EmailAndSmtpTable emailAndSmtpDetails = new EmailAndSmtpTable();
                if (m_cachedEmailAndSmtpTable == null)
                {
                    if (m_EmailTableFile != null && m_SmtpTableFile != null)
                    {
                        // load the email table from the file
                        if (System.IO.File.Exists(m_EmailTableFile))
                        {
                            emailAndSmtpDetails.m_emailTable =
                                (EmailTable)RollbackLib.src.utils.XMLUtils.DeSerializeFromXML(m_EmailTableFile, typeof(EmailTable));
                        }

                        // load the smtp table from the file
                        if (System.IO.File.Exists(m_SmtpTableFile))
                        {
                            emailAndSmtpDetails.m_smtpTable =
                                (SMTPTable)RollbackLib.src.utils.XMLUtils.DeSerializeFromXML(m_SmtpTableFile, typeof(SMTPTable));
                        }

                        addPopularSmtpEntries(emailAndSmtpDetails);

                        try
                        {
                            // decrypt the password
                            Crypto crypto = new Crypto(password);
                            foreach (EmailEntry entry in emailAndSmtpDetails.m_emailTable.m_EmailList)
                            {
                                String decrypted = crypto.Decrypt(entry.m_Password);
                                entry.m_Password = decrypted;
                            }
                        }
                        catch (Exception e)
                        {
                            log.Warn("Error decrpting the email passwords", e);
                        }
                    }

                }
                return m_cachedEmailAndSmtpTable;
            }
        }

        /// <summary>
        /// Added the most popular SMTP entries to the list.
        /// </summary>
        /// <param name="emailAndSmtpDetails"></param>
        private static void addPopularSmtpEntries(EmailAndSmtpTable emailAndSmtpDetails)
        {
            lock (emailAndSmtpDetails)
            {
                // add the default gmail, yahoo smtp entries
                if (emailAndSmtpDetails.m_smtpTable.getById(gmailSmtpKey) == null)
                {
                    SMTPEntry e = new SMTPEntry(gmailHost, "" + gmailPort, "true");
                    emailAndSmtpDetails.addSMTPEntry(e);
                }

                if (emailAndSmtpDetails.m_smtpTable.getById(yahooSmtpKey) == null)
                {
                    SMTPEntry e = new SMTPEntry(yahooHost, "" + yahooPort, "false");
                    emailAndSmtpDetails.addSMTPEntry(e);
                }

                if (emailAndSmtpDetails.m_smtpTable.getById(hotmailSmtpKey) == null)
                {
                    SMTPEntry e = new SMTPEntry(hotmailHost, "" + hotmailPort, "true");
                    emailAndSmtpDetails.addSMTPEntry(e);
                }
                m_cachedEmailAndSmtpTable = emailAndSmtpDetails;
            }
        }

        public void addSMTPEntry(SMTPEntry smtp)
        {
            lock (this)
            {
                SMTPEntry smtpFound = findSMTPData(smtp);
                if (smtpFound == null)
                {
                    m_smtpTable.m_smtpList.Add(smtp);
                }
            }
        }

        public void addEmailEntry(EmailEntry email)
        {
            lock (this)
            {
                EmailEntry emailFound = findEmailData(email);
                if (emailFound == null)
                {
                    m_emailTable.m_EmailList.Add(email);
                }
                else
                {
                    emailFound = email;
                }
            }
        }

        /// <summary>
        /// Find the SMTP data in the list and return it.  NULL if the data was not found.
        /// </summary>
        /// <param name="smtpData"></param>
        /// <returns></returns>
        private SMTPEntry findSMTPData(SMTPEntry smtpData)
        {
            SMTPEntry found = null;
            if (smtpData != null)
            {
                string key = smtpData.getKey();
                foreach (SMTPEntry d in m_smtpTable.m_smtpList)
                {
                    string key2 = d.getKey();
                    if (key.Equals(key2, StringComparison.InvariantCultureIgnoreCase))
                    {
                        found = d;
                        break;
                    }
                }
            }
            return found;
        }

        /// <summary>
        /// Find the Email id in the list and return it.  NULL if the emailid was not found.
        /// </summary>
        /// <param name="emailData"></param>
        /// <returns></returns>
        private EmailEntry findEmailData(EmailEntry emailData)
        {
            EmailEntry found = null;
            if (emailData != null)
            {
                string key = emailData.getKey();
                foreach (EmailEntry e in m_emailTable.m_EmailList)
                {
                    string key2 = e.getKey();
                    if (key.Equals(key2, StringComparison.InvariantCultureIgnoreCase))
                    {
                        found = e;
                        break;
                    }
                }
            }
            return found;
        }

        public static void SendEmail(String subject, String text, String reason, String emailId,String password)
        {
            EmailAndSmtpTable t = EmailAndSmtpTable.load();
            EmailEntry emailEntry = t.m_emailTable.getById(emailId);
            if (emailEntry != null)
            {
                SMTPEntry smtpEntry = t.m_smtpTable.getById(emailEntry.m_keyOfSmtpServerList);
                if (smtpEntry != null)
                {
                    int port = Convert.ToInt16(smtpEntry.m_ServerPort);
                    Boolean useSsl = false;
                    Boolean.TryParse(smtpEntry.m_useSSL, out useSsl);
                    log.Info("sending email EmailId='" + emailEntry.m_EmailId + "' SMTP='" + smtpEntry.getKey() + "  SSL=" + useSsl + reason);
                    Mailer.sendEmail(smtpEntry.m_ServerName, port, useSsl, emailEntry.m_EmailId, password, "rollback.support@gmail.com", subject, text);
                }
            }
        }



        public static void SendEmail(string subject, string text, string reason, string emailId)
        {
            EmailAndSmtpTable t = EmailAndSmtpTable.load();
            EmailEntry emailEntry = t.m_emailTable.getById(emailId);
            if (emailEntry != null)
            {
                SendEmail(subject, text, reason, emailId, emailEntry.m_Password);
            }
        }
    }
}
